Preface
Some R stuff:
- You can download the entire Rmd for this Markdown by clicking on the
upper right
Code button.
- You can also use the upper right
Code button to hide
all code if you just want to focus on the analysis.
Given that we cannot access the data and I was under the impression
that our coding walk-through today was somewhat helpful, I attempted to
create a template for the analysis that you can use for the data. I was
not able to access hospitalizations on the county level
from the Covid19 DataHub so I
will be using deaths inlieu of
hospitalizations for this walk-through.
This template makes the following assumptions:
The COVID-19 DataHub provides the data at three units of
aggregation: country, state, and
county. From my understanding, you are modeling data for
Greater St. Louis (i.e., St. Louis MSA). To get the data similar to what
Peter has access to, I summed both cases and deaths for counties in both
Illinois (‘Bond’, ‘Calhoun’, ‘Clinton’, ‘Jersey’, ‘Macoupin’, ‘Madison’,
‘Monroe’) and Missouri (‘Crawford’, ‘Franklin’, ‘Jefferson’, ‘Lincoln’,
‘St. Charles’, ‘St. Clair’, ‘St. Louis’, ‘St. Louis City’, ‘Warren’)
based on the Greater St. Louis
Wikipedia Page.
On top of my head, I did not remember the study period that you
had for the analysis. So I have used data starting from
Jan 04, 2021 (first Monday of the Year) for this
analysis.
For the St. Louis Health Department, I assumed that the
non_weekend holidays matched those of the New York Stock Exchange. These
were extracted using
HOLIDAY_SEQUENCE(start_date = '2021-01-04', end_date = '2022-08-01', calendar = 'NYSE')
from the tidyquant
package. The exact holiday dates from this function call are:
2021-01-18, 2021-02-15, 2021-04-02, 2021-05-31, 2021-07-05, 2021-09-06,
2021-11-25, 2021-12-24, 2022-01-17, 2022-02-21, 2022-04-15, 2022-05-30,
2022-07-04.
I kind of
eyeballed the dates for the dominant COVID-19 variant based
on the following
NY Times Article. I have used the plot within the article to define
the dominant variant to be as follows:
dominant_variant = case_when(date < ymd('2021-03-01') ~ 'Epsilon', date < ymd('2021-06-15') ~ 'Alpha', date < ymd('2021-12-15') ~ 'Delta', date >= ymd('2021-12-15') ~ 'Omicron') %>% as_factor().
This allowed me to take into account the ‘entire’ time-series rather
than just the Omicron part.
I assumed that one of the end goals from your analysis would be
forecasting future hospitalizations. So far, in our
meetings, we discussed explanatory modeling. So I divided my data into a
training and a holdout sample – primarily for the purpose of showing you
how its done. Obviously, you
can override this by ignoring the holdout and just focusing on the
training results (which you can also set to be the entire time period of
interest).
I did not lag the confirmed cases. I believe you
should test out different lags within the context of both the
auto.arima() and the other models since we would be
hypothesizing that the lagged confirmed cases would lead to future
hospitalizations. It is my understanding that the auto.arima()
does not account for the lagged relationships on the exogenous
variables. See the Lagged Predictors
in the fpp2 book. The
implications of introducing the lagged predictors on the current code
are as follows:
- The lag can be computed as follows within the mutate function:
confirmed1 = lag(confirmed, k = 1) for lag1. Changing
k would change the lag.
- The lag should also be accounted for in the
initial_time_split() to ensure a seperation between the
training and testing dataset. In that function, the default value for
lag = 0, and you would need to change it to the highest lag
you are evaluating.
I ignored potentially relevant predictors such
as the stringency_index and
people_fully_vaccinated. These are found in the
covid_tbl but I did not aggregate them for the
St. Louis MSA. So you can easily include them by adding
them to both the select() used to create
st_louis_agg_tbl in Section 3, and then summing over them
with the summarise_at() used later in the code. Other
potential predictors include: (a) an indicator variable
indicating the emergence of a new variant (e.g., this variable can have
a yes in the 2 weeks before and after we switch to a new
dominant variable in our code); (b) lagging the holiday variable (with
lags up to 1-3 weeks), with the hypothesis that gatherings and travel
during a holiday will have future effects on hospitalizations; and (c)
lagging indicators of percent of positive tests, i.e., a
spike in the percent of positive tests which may be more important than
the confirmed cases with latter waves since more people are testing at
home. These would be nice to
explore as you continue to build the model.
Questions for the Group
Based on My Preliminary Analysis Below
How should we handle negative counts in
cases/deaths/hospitalizations? See the death count on
March 29, 2021 in the plot in Section 5.1 as an
example.
Should we worry about the residuals’ departure from
normality? See the results of the
Shapiro-Wilk Test in Section 7.2.2.
Do you agree with my comment about lagged
predictors? See the second to last assumption in the Preface
section.
How much weight should we put on the fact that the rsq of
all models are small? See the last column (scrollable) in the
tabulated training and testing results in Sections 7 and 8.
Required Packages
The code chunk below contains the packages used in this analysis
# tidyverse pkg for data manipulation
if(!require(tidyverse)) {install.packages('tidyverse'); library(tidyverse)}
# readr for reading files
if(!require(readr)) {install.packages('readr'); library(readr)}
# forecast package for some quick displays and analyses
if(!require(forecast)) {install.packages('forecast'); library(forecast)}
# timetk for tidy time-series preprocessing
if(!require(timetk)) {install.packages('timetk'); library(timetk)}
# tidyquant for tidy time-series analysis
if(!require(tidyquant)) {install.packages('tidyquant'); library(tidyquant)}
# modeltime for tidy forecasting
if(!require(modeltime)) {install.packages('modeltime'); library(modeltime)}
# for interactive plots
if(!require(plotly)) {install.packages('plotly'); library(plotly)}
# for tidy machine learning
if(!require(tidymodels)) {install.packages('tidymodels'); library(tidymodels)}
# for the xgboost model
if(!require(xgboost)) {install.packages('xgboost'); library(xgboost)}
Plotting the Data for
Saint Louis
Raw Data from the
Repo
From the plot below, it is clear that this public dataset does not
contain information on hospitalizations, vent, and ICU. Thus, I will
model deaths as a function of cases. Peter, you can change the
variables based on your dataset.
It is also clear that the data is cumulative so we will difference to
have the daily new cases and deaths.
st_louis_agg_tbl %>%
pivot_longer(cols = c(confirmed, deaths, recovered, hosp, icu, vent, population),
names_to = 'statistic') %>%
ggplot(
aes(x = date, y = value)
) +
geom_line() +
facet_wrap(~ statistic, scales = 'free_y', ncol = 1) +
theme_bw() +
scale_x_date(breaks = scales::pretty_breaks(n = 12)) +
scale_y_continuous(labels = scales::comma) +
labs(title = 'Plots of the Time-Series of Potential Variables for St. Louis MSA',
subtitle = 'Hosp, ICU, Recovered and Vent have no data',
caption = 'Based on data aggregated from the COVID19 DataHub',
x = 'Date',
y = NULL)-> p1
ggplotly(p1)
Differenced Data
Starting from Jan 04, 2021
Based on our explanatory analysis of the data, there seemed to be
some large outliers in the beginning of 2021. So I am starting the
analysis from the first Monday of the year. I differenced to compute the
variables of interest. The code below plots the two time-series of
interest (confirmed cases and
confirmed deaths), color coded based on the COVID-19
dominant variant.
st_louis_agg_tbl =
st_louis_agg_tbl %>%
# keeping only relevant columns for forecasting
select(date, confirmed, deaths) %>%
# converting the cumulative counts to new
mutate(
confirmed = confirmed - lag(confirmed),
deaths = deaths - lag(deaths)
) %>%
filter(date >= ymd('2021-01-04')) %>%
mutate(
# creating a data frame of variants based on
# https://www.nytimes.com/interactive/2021/health/coronavirus-variant-tracker.html
dominant_variant =
case_when(
date < ymd('2021-03-01') ~ 'Epsilon',
date < ymd('2021-06-15') ~ 'Alpha',
date < ymd('2021-12-15') ~ 'Delta',
date >= ymd('2021-12-15') ~ 'Omicron'
) %>% as_factor(),
# creating a list of special holidays
holidays =
if_else(date %in% HOLIDAY_SEQUENCE(start_date = min(date),
end_date = max(date),
calendar = 'NYSE'),
true = 'yes',
false = 'no') %>% as_factor()
) %>%
drop_na()
st_louis_agg_tbl %>%
pivot_longer(cols = c(confirmed, deaths),
names_to = 'statistic') %>%
ggplot(
aes(x = date, y = value, color = dominant_variant)
) +
geom_line() +
facet_wrap(~ statistic, scales = 'free_y', ncol = 1) +
theme_bw() +
scale_x_date(breaks = scales::pretty_breaks(n = 12)) +
scale_y_continuous(labels = scales::comma) +
scale_color_brewer(palette = 'Paired') +
labs(x = 'Date',
y = 'New Daily Counts',
title = 'Plots of Confirmed Cases and Deaths for St. Louis MSA') -> p2
ggplotly(p2)
An important question to address (if this is in Peter’s data) is how
do we handle negative counts. A naive fix in R would be
to use the max of the value and 0 (see the pmax()).
However, this ignores the misreporting of the data.
Examining the ACF,
PACF and CCF
ACF of New
Deaths
# extract the starting week number and week-day (i.e., 1 -7 where 7 is Sunday) number
starting_day = min(st_louis_agg_tbl$date) %>% wday()
starting_week = min(st_louis_agg_tbl$date) %>% week()
# creating a timeseries for the deaths (our response of interest)
deaths_ts = st_louis_agg_tbl %>%
pull(deaths) %>%
ts(
start = c(starting_week, starting_day),
frequency = 7
)
# plotting the acf as a ggplot object while dropping the useless lag 0
acf(deaths_ts, lag.max = 42, plot = F) %>%
autoplot() +
scale_x_continuous(breaks = scales::pretty_breaks(n=7) ) +
theme_bw() +
labs(x = 'Lags in Weeks',
title = 'ACF of New Deaths for the Greater St. Louis MSA',
subtitle = 'The ACF shows a "weekly" pattern, i.e., we will need a seasonal ARIMA model')

PACF of New
Deaths
# plotting the acf as a ggplot object while dropping the useless lag 0
pacf(deaths_ts, lag.max = 42, plot = F) %>%
autoplot() +
scale_x_continuous(breaks = scales::pretty_breaks(n=7) ) +
theme_bw() +
labs(x = 'Lags in Weeks',
title = 'PACF of New Deaths for the Greater St. Louis MSA',
subtitle = 'The PACF shows a "weekly" pattern, i.e., we will need a seasonal ARIMA model')

CCF of New Deaths
and Confirmed
The CCF plot below shows a significant cross correlation between both
deaths and confirmed cases for up to 6 weeks of data. Note that the
relationship seems to be significant primarily in the negative
direction, which makes sense to me since lagged cases
are predictors of deaths but the other way around is not true past two
weeks. The data below also shows ‘weekly spikes’.
# creating a timeseries for the confirmed cases (similar to above)
confirmed_ts = st_louis_agg_tbl %>%
pull(confirmed) %>%
ts(
start = c(starting_week, starting_day),
frequency = 7
)
# plotting the CCF as a ggplot object
ccf(x = confirmed_ts, y = deaths_ts, plot = F, lag.max = 42) %>%
# converting the ccf plot to a ggplot object
autoplot() +
scale_x_continuous(breaks = scales::pretty_breaks(n=10)) +
labs(x = 'Lag in Weeks', y = 'CCF',
title = 'Cross Correlation Function of Deaths vs. Confirmed Cases',
subtitle = 'Cases seem to be predictors of deaths (more than the other way around)') +
theme_bw()

Creating time splits
for Training and Validation
Below, I have capitalized on the initial_time_split() to
split the time-series such that the first 90% of the data are used for
model building and the remaining 10% are used for validation. We
can redo this if we solely want to focus on the explanatory modeling
component.
splits = initial_time_split(st_louis_agg_tbl, prop = 0.9)
# Printing out the splits so that we know the number of observations used for model training
splits
## <Training/Testing/Total>
## <517/58/575>
# Training data start and end dates
paste('The starting and ending dates for training are',
splits$data[splits$in_id, 'date'] %>% head(n=1) %>% pull(),
'and',
splits$data[splits$in_id, 'date'] %>% tail(n=1) %>% pull(),
'respectively. For the holdout data, the starting and trainig dates are',
splits$data[splits$out_id, 'date'] %>% head(n=1) %>% pull(),
'and',
splits$data[splits$out_id, 'date'] %>% tail(n=1) %>% pull())
## [1] "The starting and ending dates for training are 2021-01-04 and 2022-06-04 respectively. For the holdout data, the starting and trainig dates are 2022-06-05 and 2022-08-01"
Training Different
Time-Series Models
In this section, I have quickly trained the following five
models:
- A univariate Auto ARIMA model with no xreg
- An Auto ARIMA model with confirmed, holidays (NYSE holidays) and
dominant variant as our xreg
- An Auto Arima Model with xgboost (using the xregs) on the Arima
errors
- The Prophet Model, originally developed by Facebook. See the Forecasting at Scale Paper
for more details.
- A linear regression model with predictors
as.numeric(date) + confirmed + holidays + dominant_variant
I have capitalized on the modeltime
package vignette to quickly run these models. Note that the
modeltime API also allows for running other machine
learning models for time-series applications.
Model Building
# a basic univariate ARIMA model using “Auto Arima” using arima_reg()
# using the modeltime pkg this will automatically pick the weekly seasonality
model_fit_arima =arima_reg() %>%
set_engine(engine = "auto_arima") %>%
fit(deaths ~ date, data = training(splits) )
## frequency = 7 observations per 1 week
# ARIMA with xreg
model_fit_arima_xreg = arima_reg() %>%
set_engine(engine = "auto_arima") %>%
# confirmed, holidays and dominant variant as our xreg
fit(
deaths ~ date + confirmed + holidays + dominant_variant,
data = training(splits)
)
## frequency = 7 observations per 1 week
# Auto Arima Model with xgboost on the Arima errors
model_fit_arima_boosted = arima_boost(
min_n = 2,
learn_rate = 0.015
) %>%
set_engine(engine = "auto_arima_xgboost") %>%
fit(deaths ~ date + confirmed + holidays + dominant_variant,
data = training(splits) )
## frequency = 7 observations per 1 week
# the prophet model used by Facebook
model_fit_prophet = prophet_reg() %>%
set_engine(engine = "prophet") %>%
fit(deaths ~ date + confirmed + holidays + dominant_variant,
data = training(splits) )
## Disabling yearly seasonality. Run prophet with yearly.seasonality=TRUE to override this.
## Disabling daily seasonality. Run prophet with daily.seasonality=TRUE to override this.
# Linear Regression
model_fit_lm = linear_reg() %>%
set_engine("lm") %>%
fit(deaths ~ as.numeric(date) + confirmed + holidays + dominant_variant,
data = training(splits) )
models_tbl = modeltime_table(
model_fit_arima,
model_fit_arima_xreg,
model_fit_arima_boosted,
model_fit_prophet,
model_fit_lm
)
models_tbl
## # Modeltime Table
## # A tibble: 5 × 3
## .model_id .model .model_desc
## <int> <list> <chr>
## 1 1 <fit[+]> ARIMA(2,1,3)(2,0,0)[7]
## 2 2 <fit[+]> REGRESSION WITH ARIMA(1,0,0)(1,0,0)[7] ERRORS
## 3 3 <fit[+]> ARIMA(5,1,1)(0,0,2)[7] W/ XGBOOST ERRORS
## 4 4 <fit[+]> PROPHET W/ REGRESSORS
## 5 5 <fit[+]> LM
If this data is close to your data, you can see that the
first non-seasonal difference was taken in the first and third
auto.arima() models. This answers the question from Allison
today.
LS0tDQp0aXRsZTogIk1vZGVsaW5nIENPVklELTE5IERlYXRocyBpbiBTYWludCBMb3VpcyBhcyBhIEZ1bmN0aW9uIG9mIENhc2VzIg0KYXV0aG9yOg0KICAtIG5hbWU6ICJGYWRlbCBNLiBNZWdhaGVkIF5bRW1haWw6IGZtZWdhaGVkQG1pYW1pb2guZWR1IHwgUGhvbmU6ICsxLTUxMy01MjktNDE4NSB8IFdlYnNpdGU6IDxhIGhyZWY9XCJodHRwczovL21pYW1pb2guZWR1L2ZzYi9kaXJlY3RvcnkvP3VwPS9kaXJlY3RvcnkvbWVnYWhlZm1cIj5NaWFtaSBVbml2ZXJzaXR5IE9mZmljaWFsPC9hPl0iDQogICAgYWZmaWxpYXRpb246IEZhcm1lciBTY2hvb2wgb2YgQnVzaW5lc3MsIE1pYW1pIFVuaXZlcnNpdHkNCmRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVCICVkLCAlWScpYCINCm91dHB1dDogDQogIGh0bWxfZG9jdW1lbnQ6DQogICAgY3NzOiBodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vZm1lZ2FoZWQvdmFjY2luZXMvbWFpbi9Db2RlL2N1c3RvbS5jc3MNCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cNCiAgICBjb2RlX2Rvd25sb2FkOiBUUlVFDQogICAgbnVtYmVyX3NlY3Rpb25zOiBUUlVFDQogICAgcGFnZWRfZGY6IFRSVUUNCiAgICB0b2M6IFRSVUUNCiAgICB0b2NfZmxvYXQ6IFRSVUUNCiAgICB0aGVtZTogcmVhZGFibGUNCiAgaW5jbHVkZXM6DQogICAgaW5faGVhZGVyOiBodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vZm1lZ2FoZWQvdmFjY2luZXMvbWFpbi9Db2RlL3N0cnVjdHVyZS50ZXgNCi0tLQ0KDQoNCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoY2FjaGUgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgIGVjaG8gPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmcgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgcHJvZ3Jlc3MgPSBGQUxTRSwgDQogICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgIGRldiA9ICdwbmcnLA0KICAgICAgICAgICAgICAgICAgICAgIGZpZy5yZXRpbmEgPSAyLA0KICAgICAgICAgICAgICAgICAgICAgIG91dC53aWR0aCA9ICcxMDAlJykNCg0KIyBpbnN0YWxsaW5nIHRoZSByZW1vdGVzIGFuZCBpY29ucyBwYWNrYWdlcyBpZiBuZWVkZWQNCmlmKCFyZXF1aXJlKHJlbW90ZXMpKSB7aW5zdGFsbC5wYWNrYWdlcygncmVtb3RlcycpOyBsaWJyYXJ5KHJlbW90ZXMpfQ0KDQppZighcmVxdWlyZShpY29ucykpew0KICByZW1vdGVzOjppbnN0YWxsX2dpdGh1YigibWl0Y2hlbGxvaGFyYXdpbGQvaWNvbnMiKQ0KICBsaWJyYXJ5KGljb25zKQ0KICBkb3dubG9hZF9mb250YXdlc29tZSgpDQogIGRvd25sb2FkX2FjYWRlbWljb25zKCkNCn0gDQoNCg0KIyBTZXR0aW5nIHRoZSByYW5kb20gc2VlZCBhbmQgY2h1bmsgZGVwZW5kZW5jaWVzDQprbml0cjo6b3B0c19jaHVuayRzZXQoY2FjaGUuZXh0cmEgPSBzZXQuc2VlZCgyMDIyKSwNCiAgICAgICAgICAgICAgICAgICAgICBhdXRvZGVwID0gVFJVRSkgDQoNCg0KYGBgDQoNCiMgUHJlZmFjZSANCg0KKipTb21lIFIgc3R1ZmY6KiogICANCg0KLSBZb3UgY2FuIGRvd25sb2FkIHRoZSBlbnRpcmUgUm1kIGZvciB0aGlzIE1hcmtkb3duIGJ5IGNsaWNraW5nIG9uIHRoZSB1cHBlciByaWdodCBgQ29kZWAgYnV0dG9uLiAgDQotIFlvdSBjYW4gYWxzbyB1c2UgdGhlIHVwcGVyIHJpZ2h0IGBDb2RlYCBidXR0b24gdG8gaGlkZSBhbGwgY29kZSBpZiB5b3UganVzdCB3YW50IHRvIGZvY3VzIG9uIHRoZSBhbmFseXNpcy4gIA0KDQpHaXZlbiB0aGF0IHdlIGNhbm5vdCBhY2Nlc3MgdGhlIGRhdGEgYW5kIEkgd2FzIHVuZGVyIHRoZSBpbXByZXNzaW9uIHRoYXQgb3VyIGNvZGluZyB3YWxrLXRocm91Z2ggdG9kYXkgd2FzIHNvbWV3aGF0IGhlbHBmdWwsIEkgYXR0ZW1wdGVkIHRvIGNyZWF0ZSBhIHRlbXBsYXRlIGZvciB0aGUgYW5hbHlzaXMgdGhhdCB5b3UgY2FuIHVzZSBmb3IgdGhlIGRhdGEuIEkgd2FzIG5vdCBhYmxlIHRvIGFjY2VzcyBgaG9zcGl0YWxpemF0aW9uc2Agb24gdGhlIGNvdW50eSBsZXZlbCBmcm9tIHRoZSBbQ292aWQxOSBEYXRhSHViXShodHRwczovL2NvdmlkMTlkYXRhaHViLmlvLykgc28gSSB3aWxsIGJlIHVzaW5nIGBkZWF0aHNgIGlubGlldSBvZiBgaG9zcGl0YWxpemF0aW9uc2AgZm9yIHRoaXMgd2Fsay10aHJvdWdoLiANCg0KVGhpcyAqKnRlbXBsYXRlIG1ha2VzIHRoZSBmb2xsb3dpbmcgYXNzdW1wdGlvbnM6KiogIA0KDQotIFRoZSBDT1ZJRC0xOSBEYXRhSHViIHByb3ZpZGVzIHRoZSBkYXRhIGF0IHRocmVlIHVuaXRzIG9mIGFnZ3JlZ2F0aW9uOiBgY291bnRyeWAsIGBzdGF0ZWAsIGFuZCBgY291bnR5YC4gRnJvbSBteSB1bmRlcnN0YW5kaW5nLCB5b3UgYXJlIG1vZGVsaW5nIGRhdGEgZm9yIEdyZWF0ZXIgU3QuIExvdWlzIChpLmUuLCBTdC4gTG91aXMgTVNBKS4gVG8gZ2V0IHRoZSBkYXRhIHNpbWlsYXIgdG8gd2hhdCBQZXRlciBoYXMgYWNjZXNzIHRvLCBJIHN1bW1lZCBib3RoIGNhc2VzIGFuZCBkZWF0aHMgZm9yIGNvdW50aWVzIGluIGJvdGggSWxsaW5vaXMgKCdCb25kJywgJ0NhbGhvdW4nLCAnQ2xpbnRvbicsICdKZXJzZXknLCAnTWFjb3VwaW4nLCAnTWFkaXNvbicsICdNb25yb2UnKSBhbmQgTWlzc291cmkgKCdDcmF3Zm9yZCcsICdGcmFua2xpbicsICdKZWZmZXJzb24nLCAnTGluY29sbicsICdTdC4gQ2hhcmxlcycsICdTdC4gQ2xhaXInLCAnU3QuIExvdWlzJywgJ1N0LiBMb3VpcyBDaXR5JywgJ1dhcnJlbicpIGJhc2VkIG9uIHRoZSBbR3JlYXRlciBTdC4gTG91aXMgV2lraXBlZGlhIFBhZ2VdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0dyZWF0ZXJfU3QuX0xvdWlzKS4gICANCg0KLSBPbiB0b3Agb2YgbXkgaGVhZCwgSSBkaWQgbm90IHJlbWVtYmVyIHRoZSBzdHVkeSBwZXJpb2QgdGhhdCB5b3UgaGFkIGZvciB0aGUgYW5hbHlzaXMuIFNvIEkgaGF2ZSB1c2VkIGRhdGEgc3RhcnRpbmcgZnJvbSBgSmFuIDA0LCAyMDIxYCAoZmlyc3QgTW9uZGF5IG9mIHRoZSBZZWFyKSBmb3IgdGhpcyBhbmFseXNpcy4gIA0KDQotIEZvciB0aGUgU3QuIExvdWlzIEhlYWx0aCBEZXBhcnRtZW50LCBJIGFzc3VtZWQgdGhhdCB0aGUgbm9uX3dlZWtlbmQgaG9saWRheXMgbWF0Y2hlZCB0aG9zZSBvZiB0aGUgTmV3IFlvcmsgU3RvY2sgRXhjaGFuZ2UuIFRoZXNlIHdlcmUgZXh0cmFjdGVkIHVzaW5nICBgSE9MSURBWV9TRVFVRU5DRShzdGFydF9kYXRlID0gJzIwMjEtMDEtMDQnLCBlbmRfZGF0ZSA9ICcyMDIyLTA4LTAxJywgY2FsZW5kYXIgPSAnTllTRScpYCBmcm9tIHRoZSBbdGlkeXF1YW50IHBhY2thZ2VdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy90aWR5cXVhbnQvdGlkeXF1YW50LnBkZikuIFRoZSBleGFjdCBob2xpZGF5IGRhdGVzIGZyb20gdGhpcyBmdW5jdGlvbiBjYWxsIGFyZTogYHIgdGlkeXF1YW50OjpIT0xJREFZX1NFUVVFTkNFKHN0YXJ0X2RhdGUgPSAnMjAyMS0wMS0wNCcsIGVuZF9kYXRlID0gCScyMDIyLTA4LTAxJywgY2FsZW5kYXIgPSAnTllTRScpYC4gIA0KDQotIEkga2luZCBvZiBgZXllYmFsbGVkIHRoZSBkYXRlcyBmb3IgdGhlIGRvbWluYW50IENPVklELTE5IHZhcmlhbnRgIGJhc2VkIG9uIHRoZSBbZm9sbG93aW5nIE5ZIFRpbWVzIEFydGljbGVdKGh0dHBzOi8vd3d3Lm55dGltZXMuY29tL2ludGVyYWN0aXZlLzIwMjEvaGVhbHRoL2Nvcm9uYXZpcnVzLXZhcmlhbnQtdHJhY2tlci5odG1sKS4gSSBoYXZlIHVzZWQgdGhlIHBsb3Qgd2l0aGluIHRoZSBhcnRpY2xlIHRvIGRlZmluZSB0aGUgZG9taW5hbnQgdmFyaWFudCB0byBiZSBhcyBmb2xsb3dzOiBgZG9taW5hbnRfdmFyaWFudCA9IGNhc2Vfd2hlbihkYXRlIDwgeW1kKCcyMDIxLTAzLTAxJykgfiAnRXBzaWxvbicsIGRhdGUgPCB5bWQoJzIwMjEtMDYtMTUnKSB+ICdBbHBoYScsIGRhdGUgPCB5bWQoJzIwMjEtMTItMTUnKSB+ICdEZWx0YScsIGRhdGUgPj0geW1kKCcyMDIxLTEyLTE1JykgfiAnT21pY3JvbicpICU+JSBhc19mYWN0b3IoKWAuIFRoaXMgYWxsb3dlZCBtZSB0byB0YWtlIGludG8gYWNjb3VudCB0aGUgJ2VudGlyZScgdGltZS1zZXJpZXMgcmF0aGVyIHRoYW4ganVzdCB0aGUgYE9taWNyb25gIHBhcnQuICANCg0KLSBJIGFzc3VtZWQgdGhhdCBvbmUgb2YgdGhlIGVuZCBnb2FscyBmcm9tIHlvdXIgYW5hbHlzaXMgd291bGQgYmUgKipmb3JlY2FzdGluZyoqIGZ1dHVyZSBob3NwaXRhbGl6YXRpb25zLiBTbyBmYXIsIGluIG91ciBtZWV0aW5ncywgd2UgZGlzY3Vzc2VkIGV4cGxhbmF0b3J5IG1vZGVsaW5nLiBTbyBJIGRpdmlkZWQgbXkgZGF0YSBpbnRvIGEgdHJhaW5pbmcgYW5kIGEgaG9sZG91dCBzYW1wbGUgLS0gcHJpbWFyaWx5IGZvciB0aGUgcHVycG9zZSBvZiBzaG93aW5nIHlvdSBob3cgaXRzIGRvbmUuIDxzcGFuIHN0eWxlPSJjb2xvcjpyZWQ7Zm9udC13ZWlnaHQ6Ym9sZCI+T2J2aW91c2x5LCB5b3UgY2FuIG92ZXJyaWRlIHRoaXMgYnkgaWdub3JpbmcgdGhlIGhvbGRvdXQgYW5kIGp1c3QgZm9jdXNpbmcgb24gdGhlIHRyYWluaW5nIHJlc3VsdHMgKHdoaWNoIHlvdSBjYW4gYWxzbyBzZXQgdG8gYmUgdGhlIGVudGlyZSB0aW1lIHBlcmlvZCBvZiBpbnRlcmVzdCkuPC9zcGFuPiAgDQoNCi0gKipJIGRpZCBub3QgbGFnIHRoZSBjb25maXJtZWQgY2FzZXMuKiogSSBiZWxpZXZlIHlvdSBzaG91bGQgdGVzdCBvdXQgZGlmZmVyZW50IGxhZ3Mgd2l0aGluIHRoZSBjb250ZXh0IG9mIGJvdGggdGhlICoqYXV0by5hcmltYSgpKiogYW5kIHRoZSBvdGhlciBtb2RlbHMgc2luY2Ugd2Ugd291bGQgYmUgaHlwb3RoZXNpemluZyB0aGF0IHRoZSBsYWdnZWQgY29uZmlybWVkIGNhc2VzIHdvdWxkIGxlYWQgdG8gZnV0dXJlIGhvc3BpdGFsaXphdGlvbnMuICoqSXQgaXMgbXkgdW5kZXJzdGFuZGluZyB0aGF0IHRoZSBhdXRvLmFyaW1hKCkgZG9lcyBub3QgYWNjb3VudCBmb3IgdGhlIGxhZ2dlZCByZWxhdGlvbnNoaXBzIG9uIHRoZSBleG9nZW5vdXMgdmFyaWFibGVzLioqIFNlZSB0aGUgW0xhZ2dlZCBQcmVkaWN0b3JzIGluIHRoZSBmcHAyIGJvb2tdKGh0dHBzOi8vb3RleHRzLmNvbS9mcHAyL2xhZ2dlZC1wcmVkaWN0b3JzLmh0bWwpLiA8c3BhbiBzdHlsZT0iY29sb3I6cmVkO2ZvbnQtd2VpZ2h0OmJvbGQiPlRoZSBpbXBsaWNhdGlvbnMgb2YgaW50cm9kdWNpbmcgdGhlIGxhZ2dlZCBwcmVkaWN0b3JzIG9uIHRoZSBjdXJyZW50IGNvZGUgYXJlIGFzIGZvbGxvd3M6PC9zcGFuPiAgIA0KICAqIFRoZSBsYWcgY2FuIGJlIGNvbXB1dGVkIGFzIGZvbGxvd3Mgd2l0aGluIHRoZSBtdXRhdGUgZnVuY3Rpb246IA0KICBgY29uZmlybWVkMSA9IGxhZyhjb25maXJtZWQsIGsgPSAxKWAgZm9yIGxhZzEuIENoYW5naW5nIGBrYCB3b3VsZCBjaGFuZ2UgdGhlIGxhZy4gIA0KICAqIFRoZSBsYWcgc2hvdWxkIGFsc28gYmUgYWNjb3VudGVkIGZvciBpbiB0aGUgYGluaXRpYWxfdGltZV9zcGxpdCgpYCB0byBlbnN1cmUgYSBzZXBlcmF0aW9uIGJldHdlZW4gdGhlIHRyYWluaW5nIGFuZCB0ZXN0aW5nIGRhdGFzZXQuIEluIHRoYXQgZnVuY3Rpb24sIHRoZSBkZWZhdWx0IHZhbHVlIGZvciBgbGFnID0gMGAsIGFuZCB5b3Ugd291bGQgbmVlZCB0byBjaGFuZ2UgaXQgdG8gdGhlIGhpZ2hlc3QgbGFnIHlvdSBhcmUgZXZhbHVhdGluZy4gDQoNCi0gSSAqKmlnbm9yZWQgcG90ZW50aWFsbHkgcmVsZXZhbnQgcHJlZGljdG9ycyoqIHN1Y2ggYXMgdGhlIGBzdHJpbmdlbmN5X2luZGV4YCBhbmQgYHBlb3BsZV9mdWxseV92YWNjaW5hdGVkYC4gVGhlc2UgYXJlIGZvdW5kIGluIHRoZSBgY292aWRfdGJsYCBidXQgSSBkaWQgbm90IGFnZ3JlZ2F0ZSB0aGVtIGZvciB0aGUgYFN0LiBMb3VpcyBNU0FgLiBTbyB5b3UgY2FuIGVhc2lseSBpbmNsdWRlIHRoZW0gYnkgYWRkaW5nIHRoZW0gdG8gYm90aCB0aGUgYHNlbGVjdCgpYCB1c2VkIHRvIGNyZWF0ZSBgc3RfbG91aXNfYWdnX3RibGAgaW4gU2VjdGlvbiAzLCBhbmQgdGhlbiBzdW1taW5nIG92ZXIgdGhlbSB3aXRoIHRoZSBgc3VtbWFyaXNlX2F0KClgIHVzZWQgbGF0ZXIgaW4gdGhlIGNvZGUuIE90aGVyICoqcG90ZW50aWFsIHByZWRpY3RvcnMgaW5jbHVkZToqKiAoYSkgYW4gaW5kaWNhdG9yIHZhcmlhYmxlIGluZGljYXRpbmcgdGhlIGVtZXJnZW5jZSBvZiBhIG5ldyB2YXJpYW50IChlLmcuLCB0aGlzIHZhcmlhYmxlIGNhbiBoYXZlIGEgYHllc2AgaW4gdGhlIDIgd2Vla3MgYmVmb3JlIGFuZCBhZnRlciB3ZSBzd2l0Y2ggdG8gYSBuZXcgZG9taW5hbnQgdmFyaWFibGUgaW4gb3VyIGNvZGUpOyAoYikgbGFnZ2luZyB0aGUgaG9saWRheSB2YXJpYWJsZSAod2l0aCBsYWdzIHVwIHRvIDEtMyB3ZWVrcyksIHdpdGggdGhlIGh5cG90aGVzaXMgdGhhdCBnYXRoZXJpbmdzIGFuZCB0cmF2ZWwgZHVyaW5nIGEgaG9saWRheSB3aWxsIGhhdmUgZnV0dXJlIGVmZmVjdHMgb24gaG9zcGl0YWxpemF0aW9uczsgYW5kIChjKSBsYWdnaW5nIGluZGljYXRvcnMgb2YgYHBlcmNlbnQgb2YgcG9zaXRpdmUgdGVzdHNgLCBpLmUuLCBhIHNwaWtlIGluIHRoZSBwZXJjZW50IG9mIHBvc2l0aXZlIHRlc3RzIHdoaWNoIG1heSBiZSBtb3JlIGltcG9ydGFudCB0aGFuIHRoZSBjb25maXJtZWQgY2FzZXMgd2l0aCBsYXR0ZXIgd2F2ZXMgc2luY2UgbW9yZSBwZW9wbGUgYXJlIHRlc3RpbmcgYXQgaG9tZS4gPHNwYW4gc3R5bGU9ImNvbG9yOnJlZDtmb250LXdlaWdodDpib2xkIj5UaGVzZSB3b3VsZCBiZSBuaWNlIHRvIGV4cGxvcmUgYXMgeW91IGNvbnRpbnVlIHRvIGJ1aWxkIHRoZSBtb2RlbC48L3NwYW4+IA0KDQoNCi0tLQ0KDQojIFF1ZXN0aW9ucyBmb3IgdGhlIEdyb3VwIEJhc2VkIG9uIE15IFByZWxpbWluYXJ5IEFuYWx5c2lzIEJlbG93DQoNCi0gKipIb3cgc2hvdWxkIHdlIGhhbmRsZSBuZWdhdGl2ZSBjb3VudHMgaW4gY2FzZXMvZGVhdGhzL2hvc3BpdGFsaXphdGlvbnM/KiogU2VlIHRoZSBkZWF0aCBjb3VudCBvbiBgTWFyY2ggMjksIDIwMjFgIGluIHRoZSBwbG90IGluIFNlY3Rpb24gNS4xIGFzIGFuIGV4YW1wbGUuICANCg0KLSAqKlNob3VsZCB3ZSB3b3JyeSBhYm91dCB0aGUgcmVzaWR1YWxzJyBkZXBhcnR1cmUgZnJvbSBub3JtYWxpdHk/KiogU2VlIHRoZSByZXN1bHRzIG9mIHRoZSBgU2hhcGlyby1XaWxrIFRlc3RgIGluIFNlY3Rpb24gNy4yLjIuIA0KDQotICoqRG8geW91IGFncmVlIHdpdGggbXkgY29tbWVudCBhYm91dCBsYWdnZWQgcHJlZGljdG9ycz8qKiBTZWUgdGhlIHNlY29uZCB0byBsYXN0IGFzc3VtcHRpb24gaW4gdGhlIFByZWZhY2Ugc2VjdGlvbi4gIA0KDQotICoqSG93IG11Y2ggd2VpZ2h0IHNob3VsZCB3ZSBwdXQgb24gdGhlIGZhY3QgdGhhdCB0aGUgcnNxIG9mIGFsbCBtb2RlbHMgYXJlIHNtYWxsPyoqIFNlZSB0aGUgbGFzdCBjb2x1bW4gKHNjcm9sbGFibGUpIGluIHRoZSB0YWJ1bGF0ZWQgdHJhaW5pbmcgYW5kIHRlc3RpbmcgcmVzdWx0cyBpbiBTZWN0aW9ucyA3IGFuZCA4Lg0KDQoNCi0tLQ0KDQoNCiMgUmVxdWlyZWQgUGFja2FnZXMNCg0KVGhlIGNvZGUgY2h1bmsgYmVsb3cgY29udGFpbnMgdGhlIHBhY2thZ2VzIHVzZWQgaW4gdGhpcyBhbmFseXNpcw0KDQpgYGB7ciBwa2dzfQ0KIyB0aWR5dmVyc2UgcGtnIGZvciBkYXRhIG1hbmlwdWxhdGlvbg0KaWYoIXJlcXVpcmUodGlkeXZlcnNlKSkge2luc3RhbGwucGFja2FnZXMoJ3RpZHl2ZXJzZScpOyBsaWJyYXJ5KHRpZHl2ZXJzZSl9DQoNCiMgcmVhZHIgZm9yIHJlYWRpbmcgZmlsZXMNCmlmKCFyZXF1aXJlKHJlYWRyKSkge2luc3RhbGwucGFja2FnZXMoJ3JlYWRyJyk7IGxpYnJhcnkocmVhZHIpfQ0KDQojIGZvcmVjYXN0IHBhY2thZ2UgZm9yIHNvbWUgcXVpY2sgZGlzcGxheXMgYW5kIGFuYWx5c2VzDQppZighcmVxdWlyZShmb3JlY2FzdCkpIHtpbnN0YWxsLnBhY2thZ2VzKCdmb3JlY2FzdCcpOyBsaWJyYXJ5KGZvcmVjYXN0KX0NCg0KIyB0aW1ldGsgZm9yIHRpZHkgdGltZS1zZXJpZXMgcHJlcHJvY2Vzc2luZw0KaWYoIXJlcXVpcmUodGltZXRrKSkge2luc3RhbGwucGFja2FnZXMoJ3RpbWV0aycpOyBsaWJyYXJ5KHRpbWV0ayl9DQoNCiMgdGlkeXF1YW50IGZvciB0aWR5IHRpbWUtc2VyaWVzIGFuYWx5c2lzDQppZighcmVxdWlyZSh0aWR5cXVhbnQpKSB7aW5zdGFsbC5wYWNrYWdlcygndGlkeXF1YW50Jyk7IGxpYnJhcnkodGlkeXF1YW50KX0NCg0KIyBtb2RlbHRpbWUgZm9yIHRpZHkgZm9yZWNhc3RpbmcNCmlmKCFyZXF1aXJlKG1vZGVsdGltZSkpIHtpbnN0YWxsLnBhY2thZ2VzKCdtb2RlbHRpbWUnKTsgbGlicmFyeShtb2RlbHRpbWUpfQ0KDQojIGZvciBpbnRlcmFjdGl2ZSBwbG90cw0KaWYoIXJlcXVpcmUocGxvdGx5KSkge2luc3RhbGwucGFja2FnZXMoJ3Bsb3RseScpOyBsaWJyYXJ5KHBsb3RseSl9DQoNCiMgZm9yIHRpZHkgbWFjaGluZSBsZWFybmluZw0KaWYoIXJlcXVpcmUodGlkeW1vZGVscykpIHtpbnN0YWxsLnBhY2thZ2VzKCd0aWR5bW9kZWxzJyk7IGxpYnJhcnkodGlkeW1vZGVscyl9DQoNCiMgZm9yIHRoZSB4Z2Jvb3N0IG1vZGVsDQppZighcmVxdWlyZSh4Z2Jvb3N0KSkge2luc3RhbGwucGFja2FnZXMoJ3hnYm9vc3QnKTsgbGlicmFyeSh4Z2Jvb3N0KX0NCmBgYA0KDQoNCi0tLQ0KDQojIEV4dHJhY3RpbmcgQ09WSUQtMTkgRGF0YQ0KDQpQZXIgdGhlIFByZWZhY2UsIEkgcmVhZCB0aGUgZGF0YSB1c2VkIGluIHRoaXMgYW5hbHlzaXMgZnJvbSB0aGUgW0NPVklELTE5IERhdGFIdWJdKGh0dHBzOi8vY292aWQxOWRhdGFodWIuaW8vYXJ0aWNsZXMvZGF0YS5odG1sKS4gVG8gbm90IHJlcXVpcmUgdGhlIGluc3RhbGxhdGlvbiBvZiB0aGUgYENPVklEMTlgIHBhY2thZ2UsIEkgaGF2ZSBkb3dubG9hZGVkIHRoZSB6aXAgZmlsZSBhbmQgdW56aXBwZWQgdXNpbmcgUi4gVGhlbiwgSSBmaWx0ZXJlZCB0aGUgZGF0YSB0byBNaXNzb3VyaSBhbmQgSWxsaW5vaXMsIHNlbGVjdGVkIHRoZSBhcHByb3ByaWF0ZSBjb3VudGllcywgYW5kIGNvbXB1dGVkIHRoZSB2YXJpYWJsZXMgdGhhdCB3ZSBhcmUgaW50ZXJlc3RlZCBpbi4NCg0KYGBge3IgcmF3X2RhdGF9DQojIGNyZWF0aW5nIGEgdGVtcCBmaWxlIGZvciBkb3dubG9hZGluZyB0aGUgZGF0YQ0KdGVtcCA9IHRlbXBmaWxlKCkgDQoNCiMgZG93bmxvYWQgdGhlIGZpbGUgdG8gdGVtcG9yYXJ5IGxvY2F0aW9uDQpkb3dubG9hZC5maWxlKCJodHRwczovL3N0b3JhZ2UuY292aWQxOWRhdGFodWIuaW8vY291bnRyeS9VU0EuY3N2LnppcCIsIHRlbXApDQoNCiMgdW56aXAgYW5kIHJlYWQgdGhlIGZpbGUNCmNvdmlkX3RibCA9IHVueih0ZW1wLCAiVVNBLmNzdiIpICU+JSANCiAgIyByZWFkaW5nIHRoZSBkYXRhIGZyb20gdGhlIENTVg0KICByZWFkX2NzdigpICU+JSANCiAgIyBmaWx0ZXJpbmcgdG8gTWlzc291cmkgYW5kIElsbGlub2lzDQogIGZpbHRlcihhZG1pbmlzdHJhdGl2ZV9hcmVhX2xldmVsXzIgJWluJSBjKCdJbGxpbm9pcycsICdNaXNzb3VyaScpICkNCg0KDQpzdF9sb3Vpc190YmwgPSBjb3ZpZF90YmwgJT4lIA0KICAjIFJlbGV2YW50IENvdW50aWVzIGluIElsbG5vaXMNCiAgZmlsdGVyKCANCiAgICAoYWRtaW5pc3RyYXRpdmVfYXJlYV9sZXZlbF8yID09ICdJbGxpbm9pcycgJg0KICAgICAgIGFkbWluaXN0cmF0aXZlX2FyZWFfbGV2ZWxfMyAlaW4lIGMoJ0JvbmQnLCAnQ2FsaG91bicsICdDbGludG9uJywgJ0plcnNleScsICdNYWNvdXBpbicsICdNYWRpc29uJywgJ01vbnJvZScpICkgfCANCiAgICAgICMgUmVsZXZhbnQgQ291bnRpZXMgaW4gTWlzc291cmkgDQogICAgICAoYWRtaW5pc3RyYXRpdmVfYXJlYV9sZXZlbF8yID09ICdNaXNzb3VyaScgJg0KICAgICAgICAgYWRtaW5pc3RyYXRpdmVfYXJlYV9sZXZlbF8zICVpbiUgYygnQ3Jhd2ZvcmQnLCAnRnJhbmtsaW4nLCAnSmVmZmVyc29uJywgJ0xpbmNvbG4nLCAnU3QuIENoYXJsZXMnLCAnU3QuIENsYWlyJywgJ1N0LiBMb3VpcycsICdTdC4gTG91aXMgQ2l0eScsICdXYXJyZW4nKSkNCiAgKQ0KDQojIEFnZ3JlZ2F0aW5nIHRoZSBjb3VudHMgYnkgZGF5IChzbyB0aGF0IHdlIGhhdmUgYW4gYXBwcm94aW1hdGlvbiBvZiB0b3RhbCBudW1iZXJzIGZvciBzdC4gbG91aXMpDQpzdF9sb3Vpc19hZ2dfdGJsID0gDQogIHN0X2xvdWlzX3RibCAlPiUgDQogICMgZ3JvdXBpbmcgYnkgZGF0ZSBzbyB3ZSBjYW4gY3JlYXRlZCBhbiBhZ2dyZWdhdGVkIHN1bW1hdGlvbiBhY3Jvc3MgYWxsIGNvdW50aWVzDQogIGdyb3VwX2J5KGRhdGUpICU+JSANCiAgc2VsZWN0KGRhdGUsIA0KICAgICAgICAgIyB2YXJpYWJsZXMgdG8gYmUgc3VtbWVkIGFjcm9zcyBhbGwgY291bnRpZXMgaW4gU3QuIExvdWlzDQogICAgICAgICBjb25maXJtZWQsIGRlYXRocywgcmVjb3ZlcmVkLCBob3NwLCBpY3UsIHZlbnQsIA0KICAgICAgICAgIyBwb3B1bGF0aW9uIHdpbGwgYmUgc3VtbWVkIG9ubHkgdG8gc2VlIGlmIHRoZSBhZ2dyZWdhdGlvbiBpcyByZWFzb25hYmxlDQogICAgICAgICAjIGkuZS4sIGlzIHRoZSB0b3RhbCBwb3AgY2xvc2UgdG8gdGhlIGFjdHVhbCBmb3IgdGhlIFN0LiBMb3VpcyBNU0ENCiAgICAgICAgIHBvcHVsYXRpb24sDQogICAgICAgICAjIG90aGVyIHZhcmlhYmxlcyB0aGF0IGNhbiBiZSBpbmNsdWRlZCBpbiB0aGUgYW5hbHlzaXMgbGF0ZXINCiAgICAgICAgIHN0cmluZ2VuY3lfaW5kZXgpICU+JSANCiAgc3VtbWFyaXNlX2F0KA0KICAgIHZhcnMoY29uZmlybWVkLCBkZWF0aHMsIHJlY292ZXJlZCwgaG9zcCwgaWN1LCB2ZW50LCBwb3B1bGF0aW9uKSwNCiAgICAuZnVucyA9IHN1bSwgbmEucm0gPSBUDQogICAgKSAlPiUgDQogIHVuZ3JvdXAoKSANCg0KIyByZW1vdmUgdGVtcCBmaWxlDQp1bmxpbmsodGVtcCkgDQoNCiMgZ2xpbXBzZSBvZiB0aGUgZGF0YQ0KZ2xpbXBzZShzdF9sb3Vpc19hZ2dfdGJsKQ0KYGBgDQoNCkZyb20gdGhlIHByaW50b3V0IG9mIHRoZSBkYXRhLCB3ZSBoYXZlIGByIG5jb2woc3RfbG91aXNfYWdnX3RibClgIHZhcmlhYmxlcyBhbmQgIGByIG5yb3coc3RfbG91aXNfYWdnX3RibClgIGRheXMgb2YgZGF0YS4gTm90ZSB0aGF0IHRoZSBgZG9taW5hbnRfdmFyaWFudGAgd2FzIGV5ZWJhbGxlZCBhcyBtZW50aW9uZWQgcHJldmlvdXNseSBhbmQgdGhlIGBob2xpZGF5c2AgdmFyaWFibGUgYXNzdW1lcyB0aGF0IHlvdXIgaGVhbHRoIGRlcGFydG1lbnQgbWF0Y2hlcyB0aGUgaG9saWRheXMgdGFrZW4gYnkgdGhlIE5ZU0UuDQoNCi0tLQ0KDQojIFBsb3R0aW5nIHRoZSBEYXRhIGZvciBTYWludCBMb3Vpcw0KDQojIyBSYXcgRGF0YSBmcm9tIHRoZSBSZXBvDQoNCkZyb20gdGhlIHBsb3QgYmVsb3csIGl0IGlzIGNsZWFyIHRoYXQgdGhpcyBwdWJsaWMgZGF0YXNldCBkb2VzIG5vdCBjb250YWluIGluZm9ybWF0aW9uIG9uIGhvc3BpdGFsaXphdGlvbnMsIHZlbnQsIGFuZCBJQ1UuIFRodXMsIEkgd2lsbCBtb2RlbCBkZWF0aHMgYXMgYSBmdW5jdGlvbiBvZiBjYXNlcy4gKipQZXRlciwgeW91IGNhbiBjaGFuZ2UgdGhlIHZhcmlhYmxlcyBiYXNlZCBvbiB5b3VyIGRhdGFzZXQqKi4NCg0KSXQgaXMgYWxzbyBjbGVhciB0aGF0IHRoZSBkYXRhIGlzIGN1bXVsYXRpdmUgc28gd2Ugd2lsbCBkaWZmZXJlbmNlIHRvIGhhdmUgdGhlIGRhaWx5IG5ldyBjYXNlcyBhbmQgZGVhdGhzLg0KDQpgYGB7ciBlZGEsIGZpZy5oZWlnaHQ9Ny41fQ0Kc3RfbG91aXNfYWdnX3RibCAlPiUgDQogIHBpdm90X2xvbmdlcihjb2xzID0gYyhjb25maXJtZWQsIGRlYXRocywgcmVjb3ZlcmVkLCBob3NwLCBpY3UsIHZlbnQsIHBvcHVsYXRpb24pLA0KICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAnc3RhdGlzdGljJykgJT4lIA0KICBnZ3Bsb3QoDQogICAgYWVzKHggPSBkYXRlLCB5ID0gdmFsdWUpDQogICkgKyANCiAgZ2VvbV9saW5lKCkgKw0KICBmYWNldF93cmFwKH4gc3RhdGlzdGljLCBzY2FsZXMgPSAnZnJlZV95JywgbmNvbCA9IDEpICsgDQogIHRoZW1lX2J3KCkgKw0KICBzY2FsZV94X2RhdGUoYnJlYWtzID0gc2NhbGVzOjpwcmV0dHlfYnJlYWtzKG4gPSAxMikpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6Y29tbWEpICsNCiAgbGFicyh0aXRsZSA9ICdQbG90cyBvZiB0aGUgVGltZS1TZXJpZXMgb2YgUG90ZW50aWFsIFZhcmlhYmxlcyBmb3IgU3QuIExvdWlzIE1TQScsDQogICAgICAgc3VidGl0bGUgPSAnSG9zcCwgSUNVLCBSZWNvdmVyZWQgYW5kIFZlbnQgaGF2ZSBubyBkYXRhJywNCiAgICAgICBjYXB0aW9uID0gJ0Jhc2VkIG9uIGRhdGEgYWdncmVnYXRlZCBmcm9tIHRoZSBDT1ZJRDE5IERhdGFIdWInLA0KICAgICAgIHggPSAnRGF0ZScsDQogICAgICAgeSA9IE5VTEwpLT4gcDENCg0KZ2dwbG90bHkocDEpDQpgYGANCg0KDQojIyBEaWZmZXJlbmNlZCBEYXRhIFN0YXJ0aW5nIGZyb20gSmFuIDA0LCAyMDIxDQoNCkJhc2VkIG9uIG91ciBleHBsYW5hdG9yeSBhbmFseXNpcyBvZiB0aGUgZGF0YSwgdGhlcmUgc2VlbWVkIHRvIGJlIHNvbWUgbGFyZ2Ugb3V0bGllcnMgaW4gdGhlIGJlZ2lubmluZyBvZiAyMDIxLiBTbyBJIGFtIHN0YXJ0aW5nIHRoZSBhbmFseXNpcyBmcm9tIHRoZSBmaXJzdCBNb25kYXkgb2YgdGhlIHllYXIuIEkgZGlmZmVyZW5jZWQgdG8gY29tcHV0ZSB0aGUgdmFyaWFibGVzIG9mIGludGVyZXN0LiBUaGUgY29kZSBiZWxvdyBwbG90cyB0aGUgdHdvIHRpbWUtc2VyaWVzIG9mIGludGVyZXN0IChgY29uZmlybWVkIGNhc2VzYCBhbmQgYGNvbmZpcm1lZCBkZWF0aHNgKSwgY29sb3IgY29kZWQgYmFzZWQgb24gdGhlIENPVklELTE5IGRvbWluYW50IHZhcmlhbnQuIA0KDQpgYGB7ciBlZGFfZGlmZn0NCnN0X2xvdWlzX2FnZ190YmwgPSANCiAgc3RfbG91aXNfYWdnX3RibCAlPiUgDQogICMga2VlcGluZyBvbmx5IHJlbGV2YW50IGNvbHVtbnMgZm9yIGZvcmVjYXN0aW5nDQogIHNlbGVjdChkYXRlLCBjb25maXJtZWQsIGRlYXRocykgJT4lIA0KICAjIGNvbnZlcnRpbmcgdGhlIGN1bXVsYXRpdmUgY291bnRzIHRvIG5ldw0KICBtdXRhdGUoDQogICAgY29uZmlybWVkID0gY29uZmlybWVkIC0gbGFnKGNvbmZpcm1lZCksDQogICAgZGVhdGhzID0gZGVhdGhzIC0gbGFnKGRlYXRocykgDQogICkgJT4lIA0KICBmaWx0ZXIoZGF0ZSA+PSB5bWQoJzIwMjEtMDEtMDQnKSkgJT4lIA0KICBtdXRhdGUoDQogICAgIyBjcmVhdGluZyBhIGRhdGEgZnJhbWUgb2YgdmFyaWFudHMgYmFzZWQgb24gDQogICAgIyBodHRwczovL3d3dy5ueXRpbWVzLmNvbS9pbnRlcmFjdGl2ZS8yMDIxL2hlYWx0aC9jb3JvbmF2aXJ1cy12YXJpYW50LXRyYWNrZXIuaHRtbA0KICAgIGRvbWluYW50X3ZhcmlhbnQgPSANCiAgICAgIGNhc2Vfd2hlbigNCiAgICAgICAgZGF0ZSA8IHltZCgnMjAyMS0wMy0wMScpIH4gJ0Vwc2lsb24nLA0KICAgICAgICBkYXRlIDwgeW1kKCcyMDIxLTA2LTE1JykgfiAnQWxwaGEnLA0KICAgICAgICBkYXRlIDwgeW1kKCcyMDIxLTEyLTE1JykgfiAnRGVsdGEnLA0KICAgICAgICBkYXRlID49IHltZCgnMjAyMS0xMi0xNScpIH4gJ09taWNyb24nDQogICAgICApICU+JSBhc19mYWN0b3IoKSwNCiAgICAjIGNyZWF0aW5nIGEgbGlzdCBvZiBzcGVjaWFsIGhvbGlkYXlzDQogICAgICAgICBob2xpZGF5cyA9IA0KICAgICAgaWZfZWxzZShkYXRlICVpbiUgSE9MSURBWV9TRVFVRU5DRShzdGFydF9kYXRlID0gbWluKGRhdGUpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVuZF9kYXRlID0gbWF4KGRhdGUpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhbGVuZGFyID0gJ05ZU0UnKSwNCiAgICAgICAgICAgICAgdHJ1ZSA9ICd5ZXMnLA0KICAgICAgICAgICAgICBmYWxzZSA9ICdubycpICU+JSBhc19mYWN0b3IoKQ0KICAgICAgICAgICApICU+JSANCiAgZHJvcF9uYSgpDQoNCnN0X2xvdWlzX2FnZ190YmwgJT4lIA0KICBwaXZvdF9sb25nZXIoY29scyA9IGMoY29uZmlybWVkLCBkZWF0aHMpLA0KICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAnc3RhdGlzdGljJykgJT4lIA0KICBnZ3Bsb3QoDQogICAgYWVzKHggPSBkYXRlLCB5ID0gdmFsdWUsIGNvbG9yID0gZG9taW5hbnRfdmFyaWFudCkNCiAgKSArIA0KICBnZW9tX2xpbmUoKSArDQogIGZhY2V0X3dyYXAofiBzdGF0aXN0aWMsIHNjYWxlcyA9ICdmcmVlX3knLCBuY29sID0gMSkgKyANCiAgdGhlbWVfYncoKSArDQogIHNjYWxlX3hfZGF0ZShicmVha3MgPSBzY2FsZXM6OnByZXR0eV9icmVha3MobiA9IDEyKSkgKyANCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6Y29tbWEpICsNCiAgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGUgPSAnUGFpcmVkJykgKw0KICBsYWJzKHggPSAnRGF0ZScsDQogICAgICAgeSA9ICdOZXcgRGFpbHkgQ291bnRzJywgDQogICAgICAgdGl0bGUgPSAnUGxvdHMgb2YgQ29uZmlybWVkIENhc2VzIGFuZCBEZWF0aHMgZm9yIFN0LiBMb3VpcyBNU0EnKSAtPiBwMg0KDQpnZ3Bsb3RseShwMikNCg0KICANCmBgYA0KDQpBbiBpbXBvcnRhbnQgcXVlc3Rpb24gdG8gYWRkcmVzcyAoaWYgdGhpcyBpcyBpbiBQZXRlcidzIGRhdGEpIGlzIGhvdyBkbyB3ZSAqKmhhbmRsZSBuZWdhdGl2ZSBjb3VudHMqKi4gQSBuYWl2ZSBmaXggaW4gUiB3b3VsZCBiZSB0byB1c2UgdGhlIG1heCBvZiB0aGUgdmFsdWUgYW5kIDAgKHNlZSB0aGUgYHBtYXgoKWApLiBIb3dldmVyLCB0aGlzIGlnbm9yZXMgdGhlIG1pc3JlcG9ydGluZyBvZiB0aGUgZGF0YS4gDQoNCg0KIyMgRXhhbWluaW5nIHRoZSBBQ0YsIFBBQ0YgYW5kIENDRiANCg0KIyMjIEFDRiBvZiBOZXcgRGVhdGhzDQoNCmBgYHtyIGFjZl9kZWF0aHN9DQojIGV4dHJhY3QgdGhlIHN0YXJ0aW5nIHdlZWsgbnVtYmVyIGFuZCB3ZWVrLWRheSAoaS5lLiwgMSAtNyB3aGVyZSA3IGlzIFN1bmRheSkgbnVtYmVyDQpzdGFydGluZ19kYXkgPSBtaW4oc3RfbG91aXNfYWdnX3RibCRkYXRlKSAlPiUgd2RheSgpDQpzdGFydGluZ193ZWVrID0gbWluKHN0X2xvdWlzX2FnZ190YmwkZGF0ZSkgJT4lIHdlZWsoKQ0KDQojIGNyZWF0aW5nIGEgdGltZXNlcmllcyBmb3IgdGhlIGRlYXRocyAob3VyIHJlc3BvbnNlIG9mIGludGVyZXN0KQ0KZGVhdGhzX3RzID0gc3RfbG91aXNfYWdnX3RibCAlPiUgDQogIHB1bGwoZGVhdGhzKSAlPiUgDQogIHRzKCANCiAgICBzdGFydCA9IGMoc3RhcnRpbmdfd2Vlaywgc3RhcnRpbmdfZGF5KSwNCiAgICBmcmVxdWVuY3kgPSA3DQogICkNCg0KIyBwbG90dGluZyB0aGUgYWNmIGFzIGEgZ2dwbG90IG9iamVjdCB3aGlsZSBkcm9wcGluZyB0aGUgdXNlbGVzcyBsYWcgMA0KYWNmKGRlYXRoc190cywgbGFnLm1heCA9IDQyLCBwbG90ID0gRikgJT4lIA0KICBhdXRvcGxvdCgpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNjYWxlczo6cHJldHR5X2JyZWFrcyhuPTcpICkgKw0KICB0aGVtZV9idygpICsNCiAgbGFicyh4ID0gJ0xhZ3MgaW4gV2Vla3MnLA0KICAgICAgIHRpdGxlID0gJ0FDRiBvZiBOZXcgRGVhdGhzIGZvciB0aGUgR3JlYXRlciBTdC4gTG91aXMgTVNBJywNCiAgICAgICBzdWJ0aXRsZSA9ICdUaGUgQUNGIHNob3dzIGEgIndlZWtseSIgcGF0dGVybiwgaS5lLiwgd2Ugd2lsbCBuZWVkIGEgc2Vhc29uYWwgQVJJTUEgbW9kZWwnKQ0KICANCmBgYA0KDQoNCiMjIyBQQUNGIG9mIE5ldyBEZWF0aHMNCg0KYGBge3IgcGFjZl9kZWF0aHN9DQojIHBsb3R0aW5nIHRoZSBhY2YgYXMgYSBnZ3Bsb3Qgb2JqZWN0IHdoaWxlIGRyb3BwaW5nIHRoZSB1c2VsZXNzIGxhZyAwDQpwYWNmKGRlYXRoc190cywgbGFnLm1heCA9IDQyLCBwbG90ID0gRikgJT4lIA0KICBhdXRvcGxvdCgpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNjYWxlczo6cHJldHR5X2JyZWFrcyhuPTcpICkgKw0KICB0aGVtZV9idygpICsNCiAgbGFicyh4ID0gJ0xhZ3MgaW4gV2Vla3MnLA0KICAgICAgIHRpdGxlID0gJ1BBQ0Ygb2YgTmV3IERlYXRocyBmb3IgdGhlIEdyZWF0ZXIgU3QuIExvdWlzIE1TQScsDQogICAgICAgc3VidGl0bGUgPSAnVGhlIFBBQ0Ygc2hvd3MgYSAid2Vla2x5IiBwYXR0ZXJuLCBpLmUuLCB3ZSB3aWxsIG5lZWQgYSBzZWFzb25hbCBBUklNQSBtb2RlbCcpDQogIA0KYGBgDQoNCg0KIyMjIENDRiBvZiBOZXcgRGVhdGhzIGFuZCBDb25maXJtZWQNCg0KVGhlIENDRiBwbG90IGJlbG93IHNob3dzIGEgc2lnbmlmaWNhbnQgY3Jvc3MgY29ycmVsYXRpb24gYmV0d2VlbiBib3RoIGRlYXRocyBhbmQgY29uZmlybWVkIGNhc2VzIGZvciB1cCB0byA2IHdlZWtzIG9mIGRhdGEuIE5vdGUgdGhhdCB0aGUgcmVsYXRpb25zaGlwIHNlZW1zIHRvIGJlIHNpZ25pZmljYW50IHByaW1hcmlseSBpbiB0aGUgbmVnYXRpdmUgZGlyZWN0aW9uLCB3aGljaCAqKm1ha2VzIHNlbnNlIHRvIG1lKiogc2luY2UgbGFnZ2VkIGNhc2VzIGFyZSBwcmVkaWN0b3JzIG9mIGRlYXRocyBidXQgdGhlIG90aGVyIHdheSBhcm91bmQgaXMgbm90IHRydWUgcGFzdCB0d28gd2Vla3MuIFRoZSBkYXRhIGJlbG93IGFsc28gc2hvd3MgJ3dlZWtseSBzcGlrZXMnLg0KDQpgYGB7ciBjY2Z9DQojIGNyZWF0aW5nIGEgdGltZXNlcmllcyBmb3IgdGhlIGNvbmZpcm1lZCBjYXNlcyAoc2ltaWxhciB0byBhYm92ZSkNCmNvbmZpcm1lZF90cyA9IHN0X2xvdWlzX2FnZ190YmwgJT4lIA0KICBwdWxsKGNvbmZpcm1lZCkgJT4lIA0KICB0cyggDQogICAgc3RhcnQgPSBjKHN0YXJ0aW5nX3dlZWssIHN0YXJ0aW5nX2RheSksDQogICAgZnJlcXVlbmN5ID0gNw0KICApDQoNCiMgcGxvdHRpbmcgdGhlIENDRiBhcyBhIGdncGxvdCBvYmplY3QNCmNjZih4ID0gY29uZmlybWVkX3RzLCB5ID0gZGVhdGhzX3RzLCBwbG90ID0gRiwgbGFnLm1heCA9IDQyKSAlPiUgDQogICMgY29udmVydGluZyB0aGUgY2NmIHBsb3QgdG8gYSBnZ3Bsb3Qgb2JqZWN0DQogIGF1dG9wbG90KCkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2NhbGVzOjpwcmV0dHlfYnJlYWtzKG49MTApKSArIA0KICBsYWJzKHggPSAnTGFnIGluIFdlZWtzJywgeSA9ICdDQ0YnLCANCiAgICAgICB0aXRsZSA9ICdDcm9zcyBDb3JyZWxhdGlvbiBGdW5jdGlvbiBvZiBEZWF0aHMgdnMuIENvbmZpcm1lZCBDYXNlcycsDQogICAgICAgc3VidGl0bGUgPSAnQ2FzZXMgc2VlbSB0byBiZSBwcmVkaWN0b3JzIG9mIGRlYXRocyAobW9yZSB0aGFuIHRoZSBvdGhlciB3YXkgYXJvdW5kKScpICsNCiAgdGhlbWVfYncoKQ0KYGBgDQoNCg0KLS0tDQoNCiMgQ3JlYXRpbmcgdGltZSBzcGxpdHMgZm9yIFRyYWluaW5nIGFuZCBWYWxpZGF0aW9uDQoNCkJlbG93LCBJIGhhdmUgY2FwaXRhbGl6ZWQgb24gdGhlIGBpbml0aWFsX3RpbWVfc3BsaXQoKWAgdG8gc3BsaXQgdGhlIHRpbWUtc2VyaWVzIHN1Y2ggdGhhdCB0aGUgZmlyc3QgOTAlIG9mIHRoZSBkYXRhIGFyZSB1c2VkIGZvciBtb2RlbCBidWlsZGluZyBhbmQgdGhlIHJlbWFpbmluZyAxMCUgYXJlIHVzZWQgZm9yIHZhbGlkYXRpb24uICoqV2UgY2FuIHJlZG8gdGhpcyBpZiB3ZSBzb2xlbHkgd2FudCB0byBmb2N1cyBvbiB0aGUgZXhwbGFuYXRvcnkgbW9kZWxpbmcgY29tcG9uZW50LioqDQoNCmBgYHtyIHRzX3NwbGl0fQ0Kc3BsaXRzID0gaW5pdGlhbF90aW1lX3NwbGl0KHN0X2xvdWlzX2FnZ190YmwsIHByb3AgPSAwLjkpDQoNCiMgUHJpbnRpbmcgb3V0IHRoZSBzcGxpdHMgc28gdGhhdCB3ZSBrbm93IHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIHVzZWQgZm9yIG1vZGVsIHRyYWluaW5nDQpzcGxpdHMNCg0KIyBUcmFpbmluZyBkYXRhIHN0YXJ0IGFuZCBlbmQgZGF0ZXMNCnBhc3RlKCdUaGUgc3RhcnRpbmcgYW5kIGVuZGluZyBkYXRlcyBmb3IgdHJhaW5pbmcgYXJlJywNCiAgICAgIHNwbGl0cyRkYXRhW3NwbGl0cyRpbl9pZCwgJ2RhdGUnXSAlPiUgaGVhZChuPTEpICU+JSBwdWxsKCksDQogICAgICAnYW5kJywgDQogICAgICBzcGxpdHMkZGF0YVtzcGxpdHMkaW5faWQsICdkYXRlJ10gJT4lIHRhaWwobj0xKSAlPiUgcHVsbCgpLA0KICAgICAgJ3Jlc3BlY3RpdmVseS4gRm9yIHRoZSBob2xkb3V0IGRhdGEsIHRoZSBzdGFydGluZyBhbmQgdHJhaW5pZyBkYXRlcyBhcmUnLA0KICAgICAgc3BsaXRzJGRhdGFbc3BsaXRzJG91dF9pZCwgJ2RhdGUnXSAlPiUgaGVhZChuPTEpICU+JSBwdWxsKCksDQogICAgICAnYW5kJywgDQogICAgICBzcGxpdHMkZGF0YVtzcGxpdHMkb3V0X2lkLCAnZGF0ZSddICU+JSB0YWlsKG49MSkgJT4lIHB1bGwoKSkNCmBgYA0KDQotLS0NCg0KIyBUcmFpbmluZyBEaWZmZXJlbnQgVGltZS1TZXJpZXMgTW9kZWxzDQoNCkluIHRoaXMgc2VjdGlvbiwgSSBoYXZlIHF1aWNrbHkgdHJhaW5lZCB0aGUgZm9sbG93aW5nIGZpdmUgbW9kZWxzOiAgDQoNCi0gQSB1bml2YXJpYXRlIEF1dG8gQVJJTUEgbW9kZWwgd2l0aCBubyB4cmVnICANCi0gQW4gQXV0byBBUklNQSBtb2RlbCB3aXRoIGNvbmZpcm1lZCwgaG9saWRheXMgKE5ZU0UgaG9saWRheXMpIGFuZCBkb21pbmFudCB2YXJpYW50IGFzIG91ciB4cmVnICANCi0gQW4gQXV0byBBcmltYSBNb2RlbCB3aXRoIHhnYm9vc3QgKHVzaW5nIHRoZSB4cmVncykgb24gdGhlIEFyaW1hIGVycm9ycyAgDQotIFRoZSBQcm9waGV0IE1vZGVsLCBvcmlnaW5hbGx5IGRldmVsb3BlZCBieSBbRmFjZWJvb2tdKGh0dHBzOi8vZmFjZWJvb2suZ2l0aHViLmlvL3Byb3BoZXQvKS4gU2VlIHRoZSBbRm9yZWNhc3RpbmcgYXQgU2NhbGUgUGFwZXJdKGh0dHBzOi8vcGVlcmouY29tL3ByZXByaW50cy8zMTkwLykgZm9yIG1vcmUgZGV0YWlscy4gIA0KLSBBIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIHdpdGggcHJlZGljdG9ycyBgYXMubnVtZXJpYyhkYXRlKSArIGNvbmZpcm1lZCArIGhvbGlkYXlzICsgZG9taW5hbnRfdmFyaWFudGANCg0KSSBoYXZlIGNhcGl0YWxpemVkIG9uIHRoZSBbbW9kZWx0aW1lIHBhY2thZ2UgdmlnbmV0dGVdKGh0dHBzOi8vYnVzaW5lc3Mtc2NpZW5jZS5naXRodWIuaW8vbW9kZWx0aW1lL2FydGljbGVzL2dldHRpbmctc3RhcnRlZC13aXRoLW1vZGVsdGltZS5odG1sKSB0byBxdWlja2x5IHJ1biB0aGVzZSBtb2RlbHMuIE5vdGUgdGhhdCB0aGUgYG1vZGVsdGltZSBBUElgIGFsc28gYWxsb3dzIGZvciBydW5uaW5nIG90aGVyIG1hY2hpbmUgbGVhcm5pbmcgbW9kZWxzIGZvciB0aW1lLXNlcmllcyBhcHBsaWNhdGlvbnMuDQoNCiMjIE1vZGVsIEJ1aWxkaW5nDQoNCmBgYHtyIG1vZGVsX2J1aWxkaW5nLCBtZXNzYWdlPVRSVUV9DQojIGEgYmFzaWMgdW5pdmFyaWF0ZSBBUklNQSBtb2RlbCB1c2luZyDigJxBdXRvIEFyaW1h4oCdIHVzaW5nIGFyaW1hX3JlZygpDQojIHVzaW5nIHRoZSBtb2RlbHRpbWUgcGtnIHRoaXMgd2lsbCBhdXRvbWF0aWNhbGx5IHBpY2sgdGhlIHdlZWtseSBzZWFzb25hbGl0eQ0KbW9kZWxfZml0X2FyaW1hID1hcmltYV9yZWcoKSAlPiUNCiAgICBzZXRfZW5naW5lKGVuZ2luZSA9ICJhdXRvX2FyaW1hIikgJT4lDQogICAgZml0KGRlYXRocyB+IGRhdGUsIGRhdGEgPSB0cmFpbmluZyhzcGxpdHMpICkNCg0KIyBBUklNQSB3aXRoIHhyZWcNCm1vZGVsX2ZpdF9hcmltYV94cmVnID0gYXJpbWFfcmVnKCkgJT4lDQogIHNldF9lbmdpbmUoZW5naW5lID0gImF1dG9fYXJpbWEiKSAlPiUNCiAgIyBjb25maXJtZWQsIGhvbGlkYXlzIGFuZCBkb21pbmFudCB2YXJpYW50IGFzIG91ciB4cmVnDQogIGZpdCgNCiAgICBkZWF0aHMgfiBkYXRlICsgY29uZmlybWVkICsgaG9saWRheXMgKyBkb21pbmFudF92YXJpYW50LA0KICAgICAgZGF0YSA9IHRyYWluaW5nKHNwbGl0cykgDQogICAgKQ0KDQojIEF1dG8gQXJpbWEgTW9kZWwgd2l0aCB4Z2Jvb3N0IG9uIHRoZSBBcmltYSBlcnJvcnMNCm1vZGVsX2ZpdF9hcmltYV9ib29zdGVkID0gYXJpbWFfYm9vc3QoDQogICAgbWluX24gPSAyLA0KICAgIGxlYXJuX3JhdGUgPSAwLjAxNQ0KKSAlPiUNCiAgICBzZXRfZW5naW5lKGVuZ2luZSA9ICJhdXRvX2FyaW1hX3hnYm9vc3QiKSAlPiUNCiAgICBmaXQoZGVhdGhzIH4gZGF0ZSArIGNvbmZpcm1lZCArIGhvbGlkYXlzICsgZG9taW5hbnRfdmFyaWFudCwNCiAgICAgICAgZGF0YSA9IHRyYWluaW5nKHNwbGl0cykgKQ0KDQojIHRoZSBwcm9waGV0IG1vZGVsIHVzZWQgYnkgRmFjZWJvb2sNCm1vZGVsX2ZpdF9wcm9waGV0ID0gcHJvcGhldF9yZWcoKSAlPiUNCiAgc2V0X2VuZ2luZShlbmdpbmUgPSAicHJvcGhldCIpICU+JQ0KICBmaXQoZGVhdGhzIH4gZGF0ZSArIGNvbmZpcm1lZCArIGhvbGlkYXlzICsgZG9taW5hbnRfdmFyaWFudCwNCiAgICAgIGRhdGEgPSB0cmFpbmluZyhzcGxpdHMpICkNCg0KIyBMaW5lYXIgUmVncmVzc2lvbiANCm1vZGVsX2ZpdF9sbSA9IGxpbmVhcl9yZWcoKSAlPiUNCiAgICBzZXRfZW5naW5lKCJsbSIpICU+JQ0KICAgIGZpdChkZWF0aHMgfiBhcy5udW1lcmljKGRhdGUpICsgY29uZmlybWVkICsgaG9saWRheXMgKyBkb21pbmFudF92YXJpYW50LA0KICAgICAgICBkYXRhID0gdHJhaW5pbmcoc3BsaXRzKSApDQoNCm1vZGVsc190YmwgPSBtb2RlbHRpbWVfdGFibGUoDQogICAgbW9kZWxfZml0X2FyaW1hLA0KICAgIG1vZGVsX2ZpdF9hcmltYV94cmVnLA0KICAgIG1vZGVsX2ZpdF9hcmltYV9ib29zdGVkLA0KICAgIG1vZGVsX2ZpdF9wcm9waGV0LA0KICAgIG1vZGVsX2ZpdF9sbQ0KKQ0KDQptb2RlbHNfdGJsDQpgYGANCg0KKipJZiB0aGlzIGRhdGEgaXMgY2xvc2UgdG8geW91ciBkYXRhLCB5b3UgY2FuIHNlZSB0aGF0IHRoZSBmaXJzdCBub24tc2Vhc29uYWwgZGlmZmVyZW5jZSB3YXMgdGFrZW4gaW4gdGhlIGZpcnN0IGFuZCB0aGlyZCBhdXRvLmFyaW1hKCkgbW9kZWxzLioqIFRoaXMgYW5zd2VycyB0aGUgcXVlc3Rpb24gZnJvbSBBbGxpc29uIHRvZGF5Lg0KDQoNCiMjIFRyYWluaW5nIFBlcmZvcm1hbmNlDQoNCiMjIyBQbG90dGluZyB0aGUgUmVzaWR1YWxzDQoNClRoZSBjb2RlIGJlbG93IGlzIHVzZWQgdG8gcGxvdCB0aGUgcmVzaWR1YWxzIGZvciBlYWNoIG9mIHRoZSBtb2RlbHMuIFRoZSBmaXJzdCB0aHJlZSBsaW5lcyBvZiBjb2RlIGFyZSB2ZXJ5IHNpbWlsYXIgdG8gdGhlIGNvZGUgc2hvd24gaW4gdGhlIHBhY2thZ2UgdmlnbmV0dGUuIFRoZSBhZGRpdGlvbmFsIGNvZGUgaXMgdG8gYWxsb3cgeW91IHRvIGN1c3RvbWl6ZSB0aGUgcGxvdCB0byB5b3VyIGxpa2luZy4gDQoNCmBgYHtyIHJlc190cmFpbmluZ192aXosIGZpZy5oZWlnaHQ9Ny41fQ0KbW9kZWxzX3RibCAlPiUNCiAgICBtb2RlbHRpbWVfY2FsaWJyYXRlKG5ld19kYXRhID0gdHJhaW5pbmcoc3BsaXRzKSkgJT4lDQogICAgbW9kZWx0aW1lX3Jlc2lkdWFscygpICU+JQ0KICAgIHBsb3RfbW9kZWx0aW1lX3Jlc2lkdWFscyguaW50ZXJhY3RpdmUgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLnR5cGUgPSAndGltZXBsb3QnKSArDQogIHNjYWxlX3hfZGF0ZShicmVha3MgPSBzY2FsZXM6OnByZXR0eV9icmVha3MoMTIpKSArDQogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gJ0RhcmsyJykgKw0KICBmYWNldF93cmFwKH4gLm1vZGVsX2Rlc2MsIG5jb2wgPSAxLCBzY2FsZXMgPSAnZnJlZScpICsNCiAgdGhlbWVfYncoKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdub25lJykgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gJ1Jlc2lkdWFscyBwbG90IGZvciB0aGUgZml2ZSBtb2RlbHMgYmFzZWQgb24gb3VyIHRyYWluaW5nIGRhdGEnLA0KICAgIHN1YnRpdGxlID0gJ1RoZSByZXNpZHVhbHMgYXJlIGxhcmdlIG9uIHRoZSBzYW1lIGRheSBpcnJlc3BlY3RpdmUgb2YgbW9kZWwnKQ0KDQpgYGANCg0KIyMjIFN0YXRpc3RpY2FsIFRlc3RzIGZvciB0aGUgUmVzaWR1YWxzDQoNClRoZSBgbW9kZWx0aW1lX3Jlc2lkdWFsc190ZXN0KClgIGNvbXB1dGVzIHRoZSByZXN1bHRzIGZyb20gNCBkaWZmZXJlbnQgc3RhdGlzdGljYWwgdGVzdHM6ICANCg0KLSAqKlNoYXBpcm8tV2lsayBUZXN0KiogdGVzdHMgdGhlIE5vcm1hbGl0eSBvZiB0aGUgcmVzaWR1YWxzLiBUaGUgTnVsbCBIeXBvdGhlc2lzIGlzIHRoYXQgdGhlIHJlc2lkdWFscyBhcmUgbm9ybWFsbHkgZGlzdHJpYnV0ZWQuIEEgbG93ICpwLSp2YWx1ZSBiZWxvdyBhIGdpdmVuIHNpZ25pZmljYW5jZSBsZXZlbCBpbmRpY2F0ZXMgdGhlIHZhbHVlcyBhcmUgKipOT1QgTm9ybWFsbHkgRGlzdHJpYnV0ZWQqKi4gIA0KDQotIEJvdGggdGhlICoqQm94LVBpZXJjZSoqIGFuZCAqKkxqdW5nLUJveCoqIFRlc3RzIGFyZSB1c2VkIHRvIHRlc3QgZm9yIHRoZSBhYnNlbmNlIG9mIGF1dG9jb3JyZWxhdGlvbiBpbiByZXNpZHVhbHMuIEEgbG93IHAtdmFsdWUgYmVsb3cgYSBnaXZlbiBzaWduaWZpY2FuY2UgbGV2ZWwgaW5kaWNhdGVzIHRoZSB2YWx1ZXMgYXJlIGF1dG9jb3JyZWxhdGVkLiAgDQoNCg0KYGBge3IgcmVzX3RyYWluaW5nX3Rlc3R9DQptb2RlbHNfdGJsICU+JQ0KICAgIG1vZGVsdGltZV9jYWxpYnJhdGUobmV3X2RhdGEgPSB0cmFpbmluZyhzcGxpdHMpKSAlPiUNCiAgICBtb2RlbHRpbWVfcmVzaWR1YWxzKCkgJT4lDQogIG1vZGVsdGltZV9yZXNpZHVhbHNfdGVzdCgpDQoNCiMgSWYgeW91IHdhbnQgdG8gZXhwbG9yZSB0aGUgcmVzaWR1YWxzIHNlcGVyYXRlbHksIHRoZXkgYXJlIHN0b3JlZCBhcyBmb2xsb3dzDQp0cmFpbmluZ19yZXNpZHVhbHMgPSBtb2RlbHNfdGJsICU+JQ0KICAgIG1vZGVsdGltZV9jYWxpYnJhdGUobmV3X2RhdGEgPSB0cmFpbmluZyhzcGxpdHMpKSAlPiUNCiAgICBtb2RlbHRpbWVfcmVzaWR1YWxzKCkgDQpgYGANCg0KVGhlIHJlc3VsdHMgYWJvdmUgaW5kaWNhdGUgdGhhdCB0aGUgcmVzaWR1YWxzIGFyZSAqKm5vdCBub3JtYWxseSBkaXN0cmlidXRlZCoqLiBIb3dldmVyLCB0aGV5IGFsc28gaW5kaWNhdGUgdGhhdCBubyBzaWduaWZpY2FudCBhdXRvY29ycmVsYXRpb24gaXMgZXhoaWJpdGVkIGluIHRoZSByZXNpZHVhbHMgYXMgaW5kaWNhdGVkIGJ5IGJvdGggdGhlICoqQm94LVBpZXJjZSoqIGFuZCAqKkxqdW5nLUJveCoqIHRlc3QgcmVzdWx0cy4gDQoNCiMjIyBNb2RlbCdzIFRyYWluaW5nIFBlcmZvcm1hbmNlDQoNCk11bHRpcGxlIGFjY3VyYWN5IG1lYXN1cmVzIGNhbiBiZSBjb21wdXRlZCBhcyBmb2xsb3dzLiANCmBgYHtyIHJlc190cmFpbmluZ19hY2N9DQptb2RlbHNfdGJsICU+JQ0KICAgIG1vZGVsdGltZV9jYWxpYnJhdGUobmV3X2RhdGEgPSB0cmFpbmluZyhzcGxpdHMpKSAlPiUNCiAgICBtb2RlbHRpbWVfYWNjdXJhY3koKSAlPiUNCiAgICB0YWJsZV9tb2RlbHRpbWVfYWNjdXJhY3koDQogICAgKQ0KYGBgDQoNCk5vdGUgdGhhdCBNQVBFIGlzIGluZmluaXRlIGluIHRoZSBhYm92ZSB0YWJsZSBzaW5jZSB0aGVyZSBhcmUgc2V2ZXJhbCBkYXlzIHdoZXJlIHRoZSBjb3VudCBvZiBgbmV3IGRlYXRocyA9IDBgLiBUaGUgTUFTRSBpbmRpY2F0ZXMgdGhhdCBhbGwgbW9kZWxzIGltcHJvdmUgb3ZlciB0aGUgbmFpdmUgZm9yZWNhc3QgYnkgYWJvdXQgMjUlLiBIb3dldmVyLCB0aGUgcnNxIGZvciBhbGwgbW9kZWxzIGlzIGxvdy4gKipNYXkgYmUgd2UgbmVlZCB0byBhZGQgYWRkaXRpb25hbCBwcmVkaWN0b3JzKiouIA0KDQoNCi0tLQ0KDQojIFRlc3RpbmcgUGVyZm9ybWFuY2UNCg0KVGhlIG5hbWUgb2YgdGhlIGZ1bmN0aW9uIGZyb20gdGhlIGBtb2RlbHRpbWVgIHBhY2thZ2UgaXMgc29tZXdoYXQgY29uZnVzaW5nLiBJdCBpcyBjYWxsZWQgYGNhbGlicmF0ZWAgYnV0IGNhbGlicmF0aW5nIGFkZHMgYSBuZXcgY29sdW1uLCBgLmNhbGlicmF0aW9uX2RhdGFgLCB3aXRoIHRoZSB0ZXN0IHByZWRpY3Rpb25zIGFuZCByZXNpZHVhbHMgaW5zaWRlLiBBIGZldyBub3RlcyBvbiBDYWxpYnJhdGlvbjogIA0KDQotIENhbGlicmF0aW9uIGlzIGhvdyBjb25maWRlbmNlIGludGVydmFscyBhbmQgYWNjdXJhY3kgbWV0cmljcyBhcmUgZGV0ZXJtaW5lZCAgDQotIENhbGlicmF0aW9uIERhdGEgaXMgc2ltcGx5IGZvcmVjYXN0aW5nIHByZWRpY3Rpb25zIGFuZCByZXNpZHVhbHMgdGhhdCBhcmUgY2FsY3VsYXRlZCBmcm9tIG91dC1vZi1zYW1wbGUgZGF0YS4gICANCi0gQWZ0ZXIgY2FsaWJyYXRpbmcsIHRoZSBjYWxpYnJhdGlvbiBkYXRhIGZvbGxvd3MgdGhlIGRhdGEgdGhyb3VnaCB0aGUgZm9yZWNhc3Rpbmcgd29ya2Zsb3cuDQoNCg0KYGBge3IgcmVjbGFpYnJhdGlvbn0NCmNhbGlicmF0aW9uX3RibCA9IG1vZGVsc190YmwgJT4lDQogICAgbW9kZWx0aW1lX2NhbGlicmF0ZSggbmV3X2RhdGEgPSB0ZXN0aW5nKHNwbGl0cykgKQ0KDQpjYWxpYnJhdGlvbl90YmwNCmBgYA0KDQpQZXIgdGhlIGRlc2NyaXB0aW9uIGFib3ZlIHRoZSBjb2RlIGNodW5rLCB0aGUgbW9kZWwgZGVzY3JpcHRpb25zIGRpZCBub3QgY2hhbmdlIGFuZCB3ZSBvbmx5IGFwcGVuZGVkIHRoZSB0ZXN0aW5nIGRhdGEuDQoNCg0KIyMgSG9sZG91dCBQZXJmb3JtYW5jZQ0KDQojIyMgUGxvdHRpbmcgdGhlIFJlc2lkdWFscw0KDQpgYGB7ciByZXNfaG9sZG91dF92aXp9DQptb2RlbHNfdGJsICU+JQ0KICAgIG1vZGVsdGltZV9jYWxpYnJhdGUobmV3X2RhdGEgPSB0ZXN0aW5nKHNwbGl0cykpICU+JQ0KICAgIG1vZGVsdGltZV9yZXNpZHVhbHMoKSAlPiUNCiAgICBwbG90X21vZGVsdGltZV9yZXNpZHVhbHMoLmludGVyYWN0aXZlID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIC50eXBlID0gJ3RpbWVwbG90JykgKw0KICBzY2FsZV94X2RhdGUoYnJlYWtzID0gc2NhbGVzOjpwcmV0dHlfYnJlYWtzKDEyKSkgKw0KICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICdEYXJrMicpICsNCiAgZmFjZXRfd3JhcCh+IC5tb2RlbF9kZXNjLCBuY29sID0gMSkgKw0KICB0aGVtZV9idygpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ25vbmUnKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAnUmVzaWR1YWxzIHBsb3QgZm9yIHRoZSBmaXZlIG1vZGVscyBiYXNlZCBvbiBvdXIgaG9sZG91dCBkYXRhJywNCiAgICBzdWJ0aXRsZSA9ICdUaGUgQVJJTUEgdHlwZSBtb2RlbHMgYXJlIG1vc3QgYWNjdXJhdGUuIExNIGlzIG92ZXItcHJlZGljdGluZyBzaW5jZSByZXNpZHVhbHMgYXJlIG1vc3RseSBuZWdhdGl2ZScpIA0KDQpgYGANCg0KIyMjIFN0YXRpc3RpY2FsIFRlc3RzIGZvciB0aGUgUmVzaWR1YWxzDQoNCmBgYHtyIHJlc19ob2xkb3V0X3Rlc3R9DQptb2RlbHNfdGJsICU+JQ0KICAgIG1vZGVsdGltZV9jYWxpYnJhdGUobmV3X2RhdGEgPSB0ZXN0aW5nKHNwbGl0cykpICU+JQ0KICAgIG1vZGVsdGltZV9yZXNpZHVhbHMoKSAlPiUNCiAgbW9kZWx0aW1lX3Jlc2lkdWFsc190ZXN0KCkNCg0KYGBgDQoNClRoZSBob2xkb3V0IHJlc3VsdHMgYXJlIGNvbnNpc3RlbnQgd2l0aCB0aGUgb2JzZXJ2YXRpb25zIG1hZGUgb24gdGhlIHRyYWluaW5nIGRhdGEuIE9uZSBleGNlcHRpb24gaXMgdGhhdCB0aGUgQVJJTUEgd2l0aCB4cmVnIChtb2RlbCAyKSBzaG93cyBjb3JyZWxhdGVkIHJlc2lkdWFscyBvbiB0aGUgaG9sZG91dCBkYXRhc2V0Lg0KDQoNCiMjIyBNb2RlbCdzIEhvbGRvdXQgUGVyZm9ybWFuY2UNCg0KTm90ZSB0aGF0IE1BUEUgaXMgaW5maW5pdGUgaW4gdGhlIGJlbG93IHRhYmxlIHNpbmNlIHRoZXJlIGFyZSBzZXZlcmFsIGRheXMgd2hlcmUgdGhlIGNvdW50IG9mIGBuZXcgZGVhdGhzID0gMGANCmBgYHtyIHJlc19ob2xkb3V0X2FjY30NCm1vZGVsc190YmwgJT4lDQogICAgbW9kZWx0aW1lX2NhbGlicmF0ZShuZXdfZGF0YSA9IHRlc3Rpbmcoc3BsaXRzKSkgJT4lDQogICAgbW9kZWx0aW1lX2FjY3VyYWN5KCkgJT4lDQogICAgdGFibGVfbW9kZWx0aW1lX2FjY3VyYWN5KA0KICAgICkNCmBgYA0KDQpUaGUgZmlyc3QgYW5kIHRoaXJkIG1vZGVscyBjb250aW51ZSB0byBiZSB0aGUgYmVzdCBtb2RlbHMuIEhvd2V2ZXIsIHRoZWlyIGltcHJvdmVtZW50IG92ZXIgdGhlIG5haXZlIGZvcmVjYXN0IChpLmUuLCByYW5kb20gd2FsaykgaXMgc21hbGxlciB0aGFuIHRoZSB0cmFpbmluZyBkYXRhc2V0Lg0KDQoNCg==